To be able to edit code and run cells, you need to run the notebook yourself. Where would you like to run the notebook?

This notebook takes about 5 minutes to run.

In the cloud (experimental)

Binder is a free, open source service that runs scientific notebooks in the cloud! It will take a while, usually 2-7 minutes to get a session.

On your computer

(Recommended if you want to store your changes.)

  1. Copy the notebook URL:
  2. Run Pluto

    (Also see: How to install Julia and Pluto)

  3. Paste URL in the Open box

Frontmatter

If you are publishing this notebook on the web, you can set the parameters below to provide HTML metadata. This is useful for search engines and social media.

Author 1

Tweening SVGs

Like Plots.jl outputs! If you generate roughly the same image twice, then tweened_svg will tween between the two images!

This was kind of quickly put together... feel free to contribute!

👀 Reading hidden code
304 μs

Example

👀 Reading hidden code
161 μs
👀 Reading hidden code
@bind wow Slider(0:1:7)
232 ms
👀 Reading hidden code
xs = sin.(0.5 * wow .* (1:.4:10))
116 ms
👀 Reading hidden code
plot(xs; ylim=(-1.1,1.1)) |> tweened_svg
Deprecated, use `AbstractPlutoDingetjes.Display.published_to_js(x)` instead of `PlutoRunner.publish_to_js(x)`.
21.7 s

GIF recording

If you're viewing this online, here is a video:

Schermopname 2022-02-12 om 02 03 13

👀 Reading hidden code
276 μs

More example!

👀 Reading hidden code
170 μs
👀 Reading hidden code
38.8 ms
👀 Reading hidden code
21.3 μs
👀 Reading hidden code
20.8 μs
@bind num Slider(1:50)
👀 Reading hidden code
5.9 ms
left = let
regenerate

p = plot(title="Hello from Pluto.jl!"; ylim=(0,1), xlim=(0,30))

plot!(p, baseline .* .5;
fillrange=zeros(20), fillopacity=.2, fillcolor="gray",
color="gray", linestyle=:dash,
label="Baseline")
plot!(p, num:num+10, big_data[num:num+10];
label="Mitigation")

p
end |> tweened_svg
👀 Reading hidden code
Deprecated, use `AbstractPlutoDingetjes.Display.published_to_js(x)` instead of `PlutoRunner.publish_to_js(x)`.
1.1 s
# left = let
# regenerate
# demo_plot(rand(20); ylim=(0,1))
# end
👀 Reading hidden code
8.0 μs

Two SVG files

👀 Reading hidden code
185 μs
@bind before FilePicker()
👀 Reading hidden code
36.4 ms
@bind after FilePicker()
👀 Reading hidden code
645 μs
Error message

MethodError: no method matching getindex(::Nothing, ::String)

Stack trace

Here is what happened, the most recent locations are first:

  1. (	before=Show(MIME"image/svg+xml"(), before["data"]),
(
before=Show(MIME"image/svg+xml"(), before["data"]),
after=Show(MIME"image/svg+xml"(), after["data"])
)
👀 Reading hidden code
---

👀 Reading hidden code
69.7 μs
Show second image:
👀 Reading hidden code
495 ms
Error message

TypeError: non-boolean (Missing) used in boolean context

Stack trace

Here is what happened, the most recent locations are first:

  1. tweened_svg((which ? after : before)["data"])
Maybe time for a break? ☕️
tweened_svg((which ? after : before)["data"])
👀 Reading hidden code
---

Appendix

👀 Reading hidden code
184 μs
begin
using Plots
Plots.default(linewidth=5, size=(600,200))
end
👀 Reading hidden code
60.6 s
using HypertextLiteral
👀 Reading hidden code
4.5 ms
using PlutoUI
👀 Reading hidden code
279 ms
better_publish_to_js (generic function with 1 method)
👀 Reading hidden code
1.6 ms
tweened_svg (generic function with 1 method)
function tweened_svg(input)
svg_data =
(input isa Vector{UInt8}) ? String(copy(input)) :
(input isa String) ? input :
repr(MIME"image/svg+xml"(), input)
@htl """
<script id="hellooozz">
const parser = new DOMParser()
const after = $(better_publish_to_js(svg_data))
const after_svg = parser.parseFromString(
after,
"image/svg+xml"
).firstElementChild

const first_time = this == null
if(first_time) {
return after_svg
}
const before_svg = this
const b_paths = Array.from(before_svg.querySelectorAll("path,polyline"))
const a_paths = Array.from(after_svg.querySelectorAll("path,polyline"))
const same_structure = a_paths.length === b_paths.length &&
a_paths.every((x,i) => x.tagName === b_paths[i].tagName)
if(!same_structure) {
console.info("Not tweening SVGs because the structures are too different.", a_paths, b_paths)
return after_svg
} else {
b_paths.forEach((path,i) => {
// if(path.tagName === "path") {

// const old_path = b_paths[i].getAttribute("d")
// const new_path = a_paths[i].getAttribute("d")

// if(old_path !== new_path) {
// console.log(i)
// const plainShapeObject = {
// el: path,
// d: old_path,
// }
// const plainShapeObjectTo = {
// el: path,
// d: new_path,
// }
// const path_clone = Wilderness.shape(
// plainShapeObject,
// plainShapeObjectTo,
// {replace: path}
// )
// // const animation = Wilderness.timeline(path_clone, {
// // iterations: Infinity,duration: 2000,
// // alternate: true
// // })
// // Wilderness.render(after_svg, animation)
// // Wilderness.play(animation)
// console.log("Playing!")
// }
// } else if(path.tagName === "polyline") {
const attr_name =
path.tagName === "polyline" ? "points" :
path.tagName === "path" ? "d" :
undefined

const old_path = b_paths[i].__pluto_current_path ?? b_paths[i].getAttribute(attr_name)
// // console.log(old_path)
const new_path = a_paths[i].getAttribute(attr_name)

if(old_path !== new_path) {
console.log("Animating!")
// const el = svg`<\${path.tagName}>
// <animate
// attributeName="\${attr_name}"
// dur="2s"
// values="\${old_path + "; " + new_path}"
// repeatCount="indefinite"
// ></animate>
// </\${path.tagName}>`

const animate = path.querySelector("animate") ?? document.createElementNS("http://www.w3.org/2000/svg", "animate")
animate.setAttribute("attributeName", attr_name)
animate.setAttribute("dur", `200ms`)
animate.setAttribute("values", `\${old_path}; \${new_path}`)
animate.setAttribute("repeatCount", `1`)
animate.setAttribute("fill", "freeze")
path.appendChild(animate)
// path.removeAttribute(attr_name)
path.__pluto_current_path = new_path
animate.beginElement()
// path.getAttributeNames().forEach(name => {
// if(name !== attr_name) {
// el.setAttribute(name, path.getAttribute(name))
// }
// })
// path.replaceWith(el)
}


// if(old_path !== new_path) {
// console.log(i)
// const plainShapeObject = {
// el: path,
// points: old_path,
// }
// const plainShapeObjectTo = {
// el: path,
// points: new_path,
// }
// const path_clone = Wilderness.shape(
// plainShapeObject,
// plainShapeObjectTo,
// {replace: path}
// )
// const animation = Wilderness.timeline(path_clone, {
// iterations: Infinity,duration: 2000,
// alternate: true
// })
// Wilderness.render(after_svg, animation)
// Wilderness.play(animation)
// console.log("Playing!")
// }
// }
})
}
return before_svg
</script>
"""
end
👀 Reading hidden code
844 μs